/*
 * dataset_impl.h
 *
 *  Created on: Feb 28, 2011
 *      Author: tqlong
 */

#ifndef DATASET_IMPL_H_
#define DATASET_IMPL_H_

#include "dataset_tpl.h"
#include <fstream>
#include <cassert>
#include <armadillo>

template <typename T>
Dataset<T>::Dataset(const std::string& fname, bool bias_ele)
  : fname_(fname), f_(fname.c_str()), bias_(bias_ele)
{
  int n;
  f_ >> n >> dim_;
  if (bias_ele) dim_++;
//  /std::cout << "n = " << n << " dim = " << dim_ << "\n";
  p_.clear();
  p_.push_back(example_type(dim_));
  char line[100];
  f_.getline(line, 100);
}

template <typename T>
void Dataset<T>::save(const std::string& fname)
{
  std::ofstream f(fname.c_str());
  save(f);
  f.close();
}

template <typename T>
void Dataset<T>::save(std::ostream& f)
{
  f << p_.size() << " " << dim_ << "\n";
  for (unsigned int i = 0; i < p_.size(); i++) {
    p_[i].save(f);
    f << "\n";
  }
}

template <typename T>
void Dataset<T>::load(const std::string& fname, bool bias_ele)
{
  std::ifstream f(fname.c_str());
  load(f, bias_ele);
  f.close();
}

template <typename T>
void Dataset<T>::load(std::istream& f, bool bias_ele)
{
  int n;
  f >> n >> dim_;
  if (bias_ele) dim_++;
  std::cout << "n = " << n << " dim = " << dim_ << "\n";
  p_.clear();
  char line[100];
  f.getline(line, 100);
  for (int i = 0; i < n; i++) {
    p_.push_back(example_type(dim_));
    p_.back().load(f);
    if (bias_ele) p_.back().ref(dim_-1) = 1;
    //std::cout << "y = " << y(i) << " x = " << x(i);
  }
}

template <typename T>
bool Dataset<T>::next()
{
  if (f_.eof()) return false;
  if (!p_.back().load(f_)) return false;
  if (bias_) p_.back().ref(dim_-1) = 1;
  //std::cout << "y = " << y() << " x = " << x();
  return true;
}

template <typename T>
void Dataset<T>::restart()
{
  f_.close();
  f_.open(fname_.c_str());
  int n;
  f_ >> n >> dim_;
  if (bias_) dim_++;
  //std::cout << "n = " << n << " dim = " << dim_ << "\n";
  p_.clear();
  p_.push_back(example_type(dim_));
  char line[100];
  f_.getline(line, 100);
}

template <typename T>
void Dataset<T>::generate(int n, int d, double noise)
{
  vec_type w(d), x(d);
  w.randn();
  double b = arma::randn(1)[0];
  w /= arma::norm(w, 2);

  dim_ = d;
  p_.clear();
  std::cout << "noise = " << noise << "\n";
  for (int i = 0; i < n; i++) {
    x.randn();
    double y = arma::dot(w, x)+b;
    double label = y>0?1:-1;
    double r = arma::randu(1)[0];
    if (r < noise)
      std::cout << "switch(" << i << ") ";
    if (r < noise) label = -label; // switch label
    p_.push_back(example_type(x, label));
//    std::cout << "y = " << label << " x = " << x << "\n";
  }
}

template <typename T>
void Dataset<T>::shuffle()
{
  if ((int)shuffle_idx_.size() != n()) {
    shuffle_idx_ = std::vector<int>(n());
    for (int i = 0; i < n(); i++)
      shuffle_idx_[i] = i;
  }
  int N = n() * n();
  for (int k = 0; k < N; k++) {
    int i = ::rand() % n(), j = ::rand() % n();
    int tmp = shuffle_idx_[i]; shuffle_idx_[i] = shuffle_idx_[j]; shuffle_idx_[j] = tmp;
  }
}

// ============= EXAMPLE ==============
template <typename T>
void Example<T>::save(const std::string& fname)
{
  std::ofstream f(fname.c_str());
  save(f);
  f.close();
}

template <typename S>
void save(S& f, const arma::vec& x)
{
  f << x.n_elem << " ";
  for (unsigned int i = 0; i < x.n_elem; i++)
    f << i << ":" << x[i] << " ";
}

template <typename S>
void load(S& f, arma::vec& x)
{
  int nnz;
  f >> nnz;
  x.zeros();
  for (int k = 0; k < nnz; k++) {
    unsigned int i;
    double val;
    char c;
    f >> i >> c >> val;
    assert(c == ':' && i < x.n_elem && i >= 0);
//    std::cout << "i = " << i << c << "val = " << val << " ";
    x[i] = val;
  }
}

template <typename T>
void Example<T>::save(std::ostream& f)
{
  f << y_ << " ";
  ::save(f, (vec_type&)(*this));
}

template <typename T>
bool Example<T>::load(const std::string& fname)
{
  std::ifstream f(fname.c_str());
  bool ret = load(f);
  f.close();
  return ret;
}

template <typename T>
bool Example<T>::load(std::istream& f)
{
  std::string line;
  std::getline(f, line);
  if (line.empty()) return false;
  std::istringstream s(line);
  s >> y_;
  ::load(s, (vec_type&)(*this));
  return true;
}

#endif /* DATASET_IMPL_H_ */
